package com.gitee.wsl.io.api

import com.gitee.wsl.reflect.TypeInfo
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.cbor.Cbor
import kotlinx.serialization.json.Json.Default.decodeFromString
import timber.log.Timber
import kotlin.reflect.KClass

interface ValueReader {

    fun <T : Any> getOrNull(key: String, type: KClass<T>): T?

    fun isExits(key: String):Boolean

    fun isNotExits(name: String):Boolean = !isExits(name)

    operator fun <T : Any> get(key:String, type: KClass<T>): T = getOrNull(key,type) ?: throw IllegalStateException("No instance for key $key")


    fun <T : Any> getOrDefault(key: String, default:T): T = getOrNull(key,default::class) ?:default

    fun getInt(key: String): Int? = getOrNull(key, Int::class)

    fun getFloat(key: String): Float? = getOrNull(key, Float::class)

    fun getLong(key: String): Long? = getOrNull(key, Long::class)

    fun getDouble(key: String): Double? = getOrNull(key, Double::class)

    fun getBoolean(key: String): Boolean? = getOrNull(key, Boolean::class)

    fun getString(key: String): String? = getOrNull(key,String::class)

    fun getByteArray(key: String): ByteArray? = getOrNull(key,ByteArray::class)
}

inline fun <reified T:Any> ValueReader.get(key: String) = get(key,T::class)

inline fun <reified T:Any> ValueReader.getOrNull(key: String) = getOrNull(key,T::class)

inline fun <reified T> ValueReader.getDecodedOrNull(key:String):T?{
    val str = getString(key)?:return null
    return runCatching<T> {
        decodeFromString(str)
        }.onFailure {
            //log error
            Timber.e(it)
        }.getOrNull()
}

fun < T : Any> ValueReader.get(name: String, type: TypeInfo) = get(name,type.type)

fun < T : Any> ValueReader.getOrNull(name: String, type: TypeInfo) = getOrNull(name,type.type as KClass<T>)

/**
 * Returns the value associated with the given key.
 *
 * @throws IllegalArgumentException if the key does not exist.
 * @throws IllegalStateException if the stored value is of the wrong type.
 */
inline fun <reified T : Any> ValueReader.requireGet(key: String): T {
    if (!this.isExits(key)) {
        throw IllegalArgumentException("Bundle has no key $key")
    }
    @Suppress("DEPRECATION")
    return getOrNull(key) as? T ?: throw IllegalStateException("Wrong type found in Bundle for key $key")
}

inline fun <reified T> ValueReader.getOrNullUseDecode(name: String, serializer: DeserializationStrategy<T>)
        = getOrNull<String>(name)?.apply { decodeFromString(serializer,this) }

inline fun <reified T> ValueReader.getOrNullUseDecode(name: String)
        = getOrNull<String>(name)?.apply { decodeFromString(this) }

@OptIn(ExperimentalSerializationApi::class)
fun <T> ValueReader.getOrNullUseDecodeByteArray(key: String, deserializer: DeserializationStrategy<T>): T? {
    return try {
        val value = getByteArray(key)?:return null
        Cbor.decodeFromByteArray(deserializer, value)
    } catch (_: Exception) {
        null
    }
}
